package com.ctlele.team.space.api;

import android.content.Context;
import android.util.Log;

import androidx.annotation.NonNull;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;

/**
 * API请求封装
 *
 * @author stewiechen
 * @date 2023-03-23
 */
public class Api {

    private static final String TAG = "Api";

    private static final OkHttpClient client;

    private static final int TIME_OUT = 30;

    private static final String baseUrl = Apis.BASE_URL;

    private static final String CONTENT_TYPE = "content-type";

    private static final String CONTENT_TYPE_JSON = "application/json;charset=utf-8";

    // TODO: 设置表单访问
    private static final String CONTENT_TYPE_URL_FORM = "x-www-form-urlencoded";

    static {
        client = new OkHttpClient.Builder()
                .connectTimeout(TIME_OUT, TimeUnit.SECONDS)
                .readTimeout(TIME_OUT, TimeUnit.SECONDS)
                .writeTimeout(TIME_OUT, TimeUnit.SECONDS)
                .pingInterval(20, TimeUnit.SECONDS)
                .followRedirects(true)
                .cookieJar(new CookieJar() {

                    private final Map<String, List<Cookie>> cookieStore = new ConcurrentHashMap<>();

                    @Override
                    public void saveFromResponse(@NonNull HttpUrl httpUrl, @NonNull List<Cookie> list) {
                        cookieStore.put(httpUrl.host(), list);
                    }

                    @NonNull
                    @Override
                    public List<Cookie> loadForRequest(@NonNull HttpUrl httpUrl) {
                        List<Cookie> cookies = cookieStore.get(httpUrl.host());
                        return cookies == null ? new ArrayList<>() : cookies;
                    }
                })
                .build();
    }

    public interface ApiCallback {

        default void onFailure(Context context, Call call, IOException e) {
        }

        default void onResponse(Context context, Call call, Response response) throws IOException {
        }
    }

    /**
     * 请求参数封装
     *
     * @author stewiechen
     * @date 2023-04-03
     */
    public static class RequestParam {

        private String name;

        private Object value;

        public RequestParam() {

        }

        public RequestParam(String name, String value) {
            this.name = name;
            this.value = value;
        }

        @Override
        public String toString() {
            return "RequestParam{" +
                    "name='" + name + '\'' +
                    ", value=" + value +
                    '}';
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }
    }

    public static WebSocket newWebSocket(String url, WebSocketListener listener) {
        Request request = new Request.Builder()
                .url(url)
                .build();

        return client.newWebSocket(request, listener);
    }

    /**
     * Get请求
     *
     * @param context  context
     * @param url      url
     * @param params   参数
     * @param callback 响应回调
     */
    public static void get(Context context, String url, List<RequestParam> params, ApiCallback callback) {
        // 创建回调对象
        Call call = client.newCall(getRequest(url, params));

        callback(context, call, callback);
    }

    /**
     * Post请求
     *
     * @param context  context
     * @param url      url
     * @param json     json格式数据
     * @param callback 响应回调
     */
    public static void postJSON(Context context, String url, String json, ApiCallback callback) {
        // 创建回调对象
        Call call = client.newCall(postJSONRequest(url, json));

        callback(context, call, callback);
    }

    /**
     * Post请求
     *
     * @param context  context
     * @param url      url
     * @param params   参数
     * @param callback 响应回调
     */
    public static void postJSON(Context context, String url, List<RequestParam> params, ApiCallback callback) {
        JSONObject obj = new JSONObject();
        for (RequestParam param : params) {
            obj.put(param.getName(), param.getValue());
        }
        String json = JSON.toJSONString(obj);
        postJSON(context, url, json, callback);
    }

    private static Request postJSONRequest(String url, String json) {
        return new Request.Builder()
                .url(baseUrl + url)
                .addHeader(CONTENT_TYPE, CONTENT_TYPE_JSON)
                .post(RequestBody.create(MediaType.parse(CONTENT_TYPE_JSON), json))
                .build();
    }

    /**
     * POST请求携带form表单
     *
     * @param context  context
     * @param url      url
     * @param params   params
     * @param callback callback
     */
    public static void postForm(Context context, String url, List<RequestParam> params, ApiCallback callback) {
        Call call = client.newCall(postFormRequest(url, params));
        callback(context, call, callback);
    }

    /**
     * DELETE请求携带form表单
     *
     * @param context  context
     * @param url      url
     * @param params   params
     * @param callback callback
     */
    public static void deleteForm(Context context, String url, List<RequestParam> params, ApiCallback callback) {
        Call call = client.newCall(deleteFormRequest(url, params));
        callback(context, call, callback);
    }

    private static Request postFormRequest(String url, List<RequestParam> params) {
        FormBody.Builder builder = new FormBody.Builder();
        for (RequestParam param : params) {
            builder.add(param.getName(), param.getValue().toString());
        }
        FormBody body = builder.build();
        return new Request.Builder()
                .url(baseUrl + url)
                .addHeader(CONTENT_TYPE, CONTENT_TYPE_URL_FORM)
                .post(body)
                .build();
    }

    private static Request deleteFormRequest(String url, List<RequestParam> params) {
        FormBody.Builder builder = new FormBody.Builder();
        for (RequestParam param : params) {
            builder.add(param.getName(), param.getValue().toString());
        }
        FormBody body = builder.build();
        return new Request.Builder()
                .url(baseUrl + url)
                .addHeader(CONTENT_TYPE, CONTENT_TYPE_URL_FORM)
                .delete(body)
                .build();
    }

    public static void postFile(Context context, String url, String fileParam, String filename, byte[] data, List<RequestParam> params, ApiCallback callback) {
        Call call = client.newCall(postFileRequest(url, fileParam, filename, data, params));
        callback(context, call, callback);
    }

    private static Request postFileRequest(String url, String fileParam, String filename, byte[] data, List<RequestParam> params) {

        MultipartBody.Builder builder = new MultipartBody.Builder();
        for (RequestParam param : params) {
            builder.addFormDataPart(param.getName(), param.getValue().toString());
        }
        RequestBody requestBody = RequestBody.create(data, MediaType.parse("multipart/form-data"));
        MultipartBody body = builder
                .setType(MultipartBody.FORM)
                .addFormDataPart(
                        fileParam,
                        filename,
                        requestBody
                ).build();

        return new Request.Builder()
                .url(baseUrl + url)
                .post(body)
                .build();
    }

    private static Request getRequest(String url, List<RequestParam> params) {
        Request.Builder builder = new Request.Builder()
                .get();

        HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + url).newBuilder();
        for (RequestParam param : params) {
            urlBuilder.addQueryParameter(param.getName(), param.getValue().toString());
        }

        builder.url(urlBuilder.build());
        return builder.build();
    }

    private static void callback(Context context, Call call, ApiCallback callback) {
        Log.i(TAG, call.request().url().toString());
        // 发起请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                callback.onFailure(context, call, e);
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                callback.onResponse(context, call, response);
            }
        });
    }
}
